package robocup.component.coach;

import java.util.LinkedList;

import robocup.component.ControllerNumbers.PLAY_MODE;
import robocup.component.infotypes.AuralInfo;
import robocup.component.infotypes.CoachSayInfo;
import robocup.component.infotypes.CoachVisualInfo;
import robocup.component.infotypes.PlayModeInfo;
import robocup.component.infotypes.PlayerSayInfo;
import robocup.component.infotypes.PlayerTypesInfo;
import robocup.component.infotypes.RefereeInfo;
import robocup.component.infotypes.SenseBodyInfo;
import robocup.component.infotypes.VisualInfo;
import robocup.component.speechacts.coachlanguage.DefineMessage;
import robocup.component.speechacts.coachlanguage.TypeConstants;

public class BroadcastCommand
    implements CoachInfoReceiver {

  // The Coach Tactic
  private OLCoachTactic            olct             = null;

  // The World Model
  private CoachWorld               cwm              = null;

  // Number of Freeform-Messages send to the server
  private int                      freeformCount    = 0;

  // This following integers stores that cycle, in which a specified message was
  // send
  private int                      lastSendRule     = 0;

  private int                      lastSendDefine   = 0;

  private int                      lastSendAdvice   = 0;

  private int                      lastSendInfo     = 0;

  private int                      lastSendMeta     = 0;

  private int                      lastSendDelete   = 0;

  // ************************************************************************************
  // The following linkedLists are message queues
  private final LinkedList<String>       freeformMessages = new LinkedList<String>();

  private final LinkedList<CoachSayInfo> ruleMessages     = new LinkedList<CoachSayInfo>();

  private final LinkedList<CoachSayInfo> adviceMessages   = new LinkedList<CoachSayInfo>();

  private final LinkedList<CoachSayInfo> infoMessages     = new LinkedList<CoachSayInfo>();

  private final LinkedList<CoachSayInfo> metaMessages     = new LinkedList<CoachSayInfo>();

  private final LinkedList<CoachSayInfo> deleteMessages   = new LinkedList<CoachSayInfo>();

  private final LinkedList<CoachSayInfo> defineMessages   = new LinkedList<CoachSayInfo>();
  // **************************************************************************************

  // This array is special only for the OpponentFormationAnalyzer
  private final DefineMessage[]          oppFormation     = new DefineMessage[11];

  /**
   * The only constructor
   * 
   * @param olct
   *          The OLCoachTactic
   */
  public BroadcastCommand(
      final OLCoachTactic olct) {

    this.olct = olct;
    this.cwm = olct.getCwm();

  }

  /**
   * This Method is only for the Opponent Formation Analyzer to pull the
   * messages into the queue.
   * 
   * @param dm
   *          The define message
   * @param player
   *          The player number
   */
  public void appendOppFormation(final DefineMessage dm,
      final int player) {

    this.oppFormation[player - 1] = dm;
    if (this.defineMessages.isEmpty()) {
      final CoachSayInfo csi = new CoachSayInfo(-1);
      this.defineMessages.add(csi);
    }
  }

  /**
   * This Method puts a Freeform Message into the message queue
   * 
   * @param message
   *          The freeform message
   * 
   */
  public void appendFreeform(String message) {

    if (this.freeformMessages.isEmpty()) {
      this.freeformMessages.add(message);
    }
    else if ((this.cwm.getSConf().SAY_COACH_MSG_SIZE - this.freeformMessages
        .getLast().length()) > message.length()) {
      message = this.freeformMessages.getLast() + message;
      this.freeformMessages.removeLast();
      this.freeformMessages.add(message);
    }
    else {
      this.freeformMessages.add(message);
    }
  }

  /**
   * This Method puts a CLang Message into the message queue
   * 
   * @param csi
   *          A CoachSayInfo Object is needed.
   * 
   */
  public void appendCSI(final CoachSayInfo csi) {

    final TypeConstants.MESSAGETYPE type = csi.getMessageType();

    switch (type) {

      case ADVICE:
        this.adviceMessages.add(csi);
        break;

      case DEFINE:
        if (this.defineMessages.isEmpty()) {
          this.defineMessages.add(csi);
        }
        else {
          this.defineMessages.getLast().addDefineList(csi.getDefineList());
        }
        break;

      case DELETE:
        this.deleteMessages.add(csi);
        break;

      case INFO:
        this.infoMessages.add(csi);
        break;

      case META:
        this.metaMessages.add(csi);
        break;

      case RULE:
        this.ruleMessages.add(csi);
        break;

    }

  }

  /**
   * This method coordinates and finally sends the Messages to the Server
   */
  public void sayAction() {

    if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
        .getVisualCycle() - this.lastSendDefine) >= this.cwm.getSConf().CLANG_WIN_SIZE)) && (!this.defineMessages
        .isEmpty())) {

      for (final DefineMessage dm : this.oppFormation) {
        if (dm != null) {
          this.defineMessages.getFirst().addDefine(dm);
        }
      }
      this.olct.sendDirect(this.defineMessages.removeFirst().toString());

      this.lastSendDefine = this.cwm.getVisualCycle();
      System.out
          .println(this.cwm.getVisualCycle() + ". Coach is sending broadcasted define messages...");
    }

    if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
        .getVisualCycle() - this.lastSendInfo) >= this.cwm.getSConf().CLANG_WIN_SIZE)) && (!this.infoMessages
        .isEmpty())) {
      this.olct.sendDirect(this.infoMessages.removeFirst().toString());
      this.lastSendInfo = this.cwm.getVisualCycle();
      System.out
          .println(this.cwm.getVisualCycle() + ". Coach is sending broadcasted info messages...");
    }

    if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
        .getVisualCycle() - this.lastSendMeta) >= this.cwm.getSConf().CLANG_WIN_SIZE)) && (!this.metaMessages
        .isEmpty())) {
      this.olct.sendDirect(this.metaMessages.removeFirst().toString());
      this.lastSendMeta = this.cwm.getVisualCycle();
      System.out
          .println(this.cwm.getVisualCycle() + ". Coach is sending broadcasted meta messages...");
    }

    if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
        .getVisualCycle() - this.lastSendAdvice) >= this.cwm.getSConf().CLANG_WIN_SIZE)) && (!this.adviceMessages
        .isEmpty())) {
      this.olct.sendDirect(this.adviceMessages.removeFirst().toString());
      this.lastSendAdvice = this.cwm.getVisualCycle();
      System.out
          .println(this.cwm.getVisualCycle() + ". Coach is sending broadcasted advice messages...");
    }

    if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
        .getVisualCycle() - this.lastSendRule) >= this.cwm.getSConf().CLANG_WIN_SIZE)) && (!this.ruleMessages
        .isEmpty())) {
      this.olct.sendDirect(this.ruleMessages.removeFirst().toString());
      this.lastSendRule = this.cwm.getVisualCycle();
      System.out
          .println(this.cwm.getVisualCycle() + ". Coach is sending broadcasted rule messages...");
    }

    if (((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || ((this.cwm
        .getVisualCycle() - this.lastSendDelete) >= this.cwm.getSConf().CLANG_WIN_SIZE)) && (!this.deleteMessages
        .isEmpty())) {
      this.olct.sendDirect(this.deleteMessages.removeFirst().toString());
      this.lastSendDelete = this.cwm.getVisualCycle();
      System.out
          .println(this.cwm.getVisualCycle() + ". Coach is sending broadcasted delete messages...");
    }

    if ((this.cwm.getPlayMode() != PLAY_MODE.PLAY_ON) || (this.cwm
        .getPlayOnCycle() > this.cwm.getSConf().FREEFORM_WAIT_PERIOD) && (this.cwm
        .getPlayOnCycle() < (this.cwm.getSConf().FREEFORM_WAIT_PERIOD + this.cwm
        .getSConf().FREEFORM_SEND_PERIOD)) && (this.freeformCount < this.cwm
        .getSConf().SAY_COACH_COUNT_MAX)) {

      if (!this.freeformMessages.isEmpty()) {
        final String str = "(say (freeform \"" + this.freeformMessages.removeFirst() + "\"))";
        this.olct.sendDirect(str);
        // System.out.println(this.cwm.getVisualCycle() + ". Coach is sending
        // broadcasted freeform messages...");
        this.freeformCount++;
      }

    }

  }

  /**
   * Called by Perception at the arrival of Player Types.
   * 
   * @param info
   */
  public void update(final PlayerTypesInfo info) {

  }

  /**
   * Called by Perception at the arrival of an AuralInfo.
   * 
   * @param info
   */
  public void update(final AuralInfo info) {

  }

  /**
   * Called by Perception at the arrival of a VisualInfo.
   * 
   * @param info
   */
  public void update(final CoachVisualInfo info) {

    this.sayAction();
  }

  /* (non-Javadoc)
   * @see robocup.component.infotypes.InfoReceiver#update(robocup.component.infotypes.SenseBodyInfo)
   */
  public void update(final SenseBodyInfo info) {

    // TODO Auto-generated method stub
    
  }

  /* (non-Javadoc)
   * @see robocup.component.infotypes.InfoReceiver#update(robocup.component.infotypes.VisualInfo)
   */
  public void update(final VisualInfo info) {

    // TODO Auto-generated method stub
    
  }

  /* (non-Javadoc)
   * @see robocup.component.infotypes.InfoReceiver#update(robocup.component.infotypes.RefereeInfo)
   */
  public void update(final RefereeInfo info) {

    // TODO Auto-generated method stub
    
  }

  /* (non-Javadoc)
   * @see robocup.component.infotypes.InfoReceiver#update(robocup.component.infotypes.PlayModeInfo)
   */
  public void update(final PlayModeInfo info) {

    // TODO Auto-generated method stub
    
  }

  /* (non-Javadoc)
   * @see robocup.component.infotypes.InfoReceiver#update(robocup.component.infotypes.PlayerSayInfo)
   */
  public void update(final PlayerSayInfo info) {

    // TODO Auto-generated method stub
    
  }
}
